package edu.towson.forensics.securephone;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

public class SecurePhoneConnection extends Service {
  	
  ServerSocket serverSocket;
  Socket clientSocket = null;
  PrintWriter out = null;
  BufferedReader in = null;
  
  Thread timerThread=null;
//  private boolean flagJustStart;
  
  private long startTime=0;
  
  private int unSecureCode=0,notSecureCode=0;
  
  protected static final String TAG_FOR_LOG_FILE="SecurePhone";
//  protected static final int PHONE_LISTENING_PORT_NUMBER = 22221;
  protected static final int CONNECTION_ESTABLISHED_NOTIFICATION_ID = 1;
  
  //wait time for pc to send an intent and rest
  private final long WAIT_TIME_FOR_GATEWAY= 10 * 1000;   //TODO should change
  
  private final long WAIT_TIME_FOR_COMMUNICATION= 10 * 1000;
  
  CommunicationRunnable communicationRunnable=null;
  
  Thread communicationThread=null;
  
  
  private class CommunicationRunnable implements Runnable {

	private int phonePortNumber=0;
	
	CommunicationRunnable(int portno)
	{
		phonePortNumber=portno;
	}	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		startServer(phonePortNumber);
	}	
  } 
  
  private class TimerRunnable implements Runnable {

	private long timeoutValue=0;
	
	TimerRunnable(long timeout)
	{
		timeoutValue=timeout;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(timeoutValue);
			Handler handle=new Handler(Looper.getMainLooper());
			handle.post(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
				unSecureCode=0;
				UnSecurePhone();					
				}
			});
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			return;				
		}
	}
	  
  }
  
    
  //** Start Service for first time which start timing for receiving Intent from PC
  @Override
  public void onCreate() {    
//	  startTime=-1 * WAIT_TIME_FOR_PC;
//	  flagJustStart=true;
      Toast.makeText(this, "onCreate()", Toast.LENGTH_SHORT).show();
      
      TimerRunnable timerRunnable= new TimerRunnable(WAIT_TIME_FOR_GATEWAY);
      /*     
      Runnable timerRunnable= new Runnable() {
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			try {
				Thread.sleep(WAIT_TIME_FOR_PC);
				Handler handle=new Handler(Looper.getMainLooper());
				handle.post(new Runnable() {
					
					@Override
					public void run() {
						// TODO Auto-generated method stub
					unSecureCode=0;
					UnSecurePhone();					
					}
				});
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				return;				
			}
		}
	};
	*/
	
	timerThread=new Thread(timerRunnable);
	timerThread.start();	
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      //** parameters which has to send to phone for first step before timeout
      String hostIpOrName=null;
      int hostPortNo=-1;
  
	  //** Get IP and Port by Intent  
	  try
	  {
      hostIpOrName= intent.getStringExtra("hostIpOrName");        //IP Address of host	      
      hostPortNo = Integer.parseInt(intent.getStringExtra("hostPortNo"));   //Host Port Number
	  }
	  
	  //** If intent is not original and contain the predicted data
	  catch (Exception e)
	  {				
		  Log.e(TAG_FOR_LOG_FILE,"error during reading intent extras, " + e.getMessage());
		  unSecureCode=1;
		  UnSecurePhone();
	  }
	  
	  if (hostPortNo>=0 && hostIpOrName.equals("GatewayService"))
	  {
		//!0 and !-1 and !service start from other than broadcast receiver
		  startTime=intent.getLongExtra("GatewayReceivedTime", 0);
		  
		  Toast.makeText(this, "Gateway Received", Toast.LENGTH_SHORT).show();
		  
		  communicationRunnable=new CommunicationRunnable(hostPortNo);
		  communicationThread=new Thread(communicationRunnable);		  
		  communicationThread.start();
		  
		  timerThread=null;
		  startTime=System.currentTimeMillis();
		  timerThread=new Thread(new TimerRunnable(WAIT_TIME_FOR_COMMUNICATION));
	  }
	  else //** match when service starts from Broadcast receiver
		  if (hostPortNo>=0 && hostIpOrName.equals("BroadcastReceiver"))
		  {
		  startTime=intent.getLongExtra("BroadcastReceivedTime", 0);    	  

          Toast.makeText(this, "Broadcast Received", Toast.LENGTH_SHORT).show();

        	  while (System.currentTimeMillis()-startTime>WAIT_TIME_FOR_GATEWAY);
		  }
		  else
		  {
		  unSecureCode=2;
    	  UnSecurePhone(); 
		  }   
		
	      if ((System.currentTimeMillis()-startTime>WAIT_TIME_FOR_GATEWAY) && hostIpOrName=="BroadcastReceiver" )
	      {	    	  
	    	  /** If there is no intent from machine has been received it would consider as not secure machine
	    	   * which would be connection of phone to unsecured PC or other tool 
	    	   */
	    	  
	    	  notSecureCode=0;
	    	  NotSecurePhone();
	      }
      
      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }
  
  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); 
  }

	//** Establish connection
	public void startServer(int port) { //Toast.makeText(this, "I wrote it." , Toast.LENGTH_SHORT).show();
		ServerSocket listener = null;		
		try {
			listener = new ServerSocket(port);
		} catch (IOException e) {
			Log.e(TAG_FOR_LOG_FILE, e.toString());
		}
		Socket server = null;
		BufferedReader in = null;
		BufferedWriter out = null;
		Handler handler=null;		
		
		// start the socket server
			try {
				server = listener.accept();
				in =new BufferedReader(new InputStreamReader(server.getInputStream()));
				out = new BufferedWriter(new OutputStreamWriter(server.getOutputStream()));
				
				handler=new Handler(Looper.getMainLooper());
				
				String cmd = null;
				
			while (true){
				while ((cmd = in.readLine()) != null)
				try { 							
					//take action related to received command from PC
					switch(Command.valueOf(cmd)) {

					case finish:						
						in.close(); out.close(); server.close();listener.close();
						return;
//						stopSecurePhoneService(getApplicationContex);//t());
//						break;
						
					case phoneMacAddress:

						macAddress(out);						
						checkForSecurePhone(Key.pcMacAddress, in,out);
						break;
						
					case imei:
						out.write("IMEI");						
						out.newLine();
						out.flush();
						break;
						
					default :
						Log.e(TAG_FOR_LOG_FILE,cmd+" is not a defined command");
						break;
					}								
			
				} catch (IllegalArgumentException e) {
					Log.d(TAG_FOR_LOG_FILE, "unknown command : " + cmd);
				}
				
				}
			} catch (IOException e) {
				Log.e(TAG_FOR_LOG_FILE, e.toString());
			} finally {
				Log.d(TAG_FOR_LOG_FILE, "socket close");
				try {
					in.close(); out.close(); server.close();listener.close();
				} catch (IOException e) {
					Log.e(TAG_FOR_LOG_FILE, e.toString());
				}
			}
		}
			  
	
	  private void notifying() {
		//** shows a notification by ic_notification_connection_established icon in res
		  NotificationManager nfManager=(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
		  Notification nf=new Notification(R.drawable.ic_notification_connection_established,
				  							"Secure connection has been established", System.currentTimeMillis());
		  
		  nfManager.notify(CONNECTION_ESTABLISHED_NOTIFICATION_ID, nf);
		  }  
	  
		//** function to return phones WIFI mac address if it exist otherwise NULL 
		protected void macAddress(BufferedWriter outBuffer) {
			
			WifiManager wifiMgr= (WifiManager)  getApplicationContext().getSystemService(Context.WIFI_SERVICE);
			WifiInfo wifiInformation = wifiMgr.getConnectionInfo();
			try{
			outBuffer.write(wifiInformation.getMacAddress());
			outBuffer.newLine();
			outBuffer.flush();			
			}
			catch (IOException e)
			{
				Log.e(TAG_FOR_LOG_FILE, e.toString());
			}
		}	

		protected void checkForSecurePhone(Key requestOfPC,BufferedReader input, BufferedWriter output )
		{				
//			output.write(String.valueOf(requestOfPC));
//			output.newLine();
//			output.flush();
			try {
				while(!input.ready());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				Log.e(TAG_FOR_LOG_FILE, e.toString());
			}
			if (!ProvidedFunctions.chkKeyLock(requestOfPC, input.toString()));
			//	Toast.makeText(getApplicationContext(),input.toString(),Toast.LENGTH_SHORT).show();
			
		}
	  
		/* unSecureCode 
		 * 1: invalid Extras in intent , 2: incomplete Extras 
		 * 11:Invalid PC MAC address
		 */	
	  	//** PC is recognized as unsecured PC 
	  	protected void UnSecurePhone(){
			Toast.makeText(this, "UnSecurePC "+String.valueOf(unSecureCode), Toast.LENGTH_LONG).show();
			stopSecurePhoneService(this);
		}
		
		/* notSecureCode
		 * 0: Timeout for receive intent from PC
		 */
		//** PC is recognized as unsecured PC 
		protected void NotSecurePhone(){
			Toast.makeText(this, "Not SecurePC "+String.valueOf(notSecureCode), Toast.LENGTH_LONG).show();
			stopSecurePhoneService(this);
		}
		
		//** Stop existing service and all instance of this service
		protected static void stopSecurePhoneService(Context context)
		{
			context.stopService(new Intent("edu.towson.forensics.securephone.action").addCategory(Intent.CATEGORY_DEFAULT)
					.addCategory("edu.towson.forensics.securephone.category"));
			
			//stop gateway service
			context.stopService(new Intent("edu.towson.forensics.securephone.gateway.action")
														.addCategory(Intent.CATEGORY_DEFAULT)
														.addCategory("edu.towson.forensics.securephone.category"));
		}
}

